#include "rlglobal.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QTest>
#include <QLineEdit>
#include <QtSerialPort/qserialportinfo.h>

#define PREF_COM			"PORT"
#define PREF_PORT_NAME		"PORT_NAME"

/*
	Протокол передачи. 
	Используем сигнатуру начала пакета и его окончания - чтобы избавиться от поля с длиной пакета - вообщем упращаем (про сигнатуру конца пакета).
	Сигнатура начала и конца по размеру совпадает и лежит в диапазоне аскои. Команды однобайтовые, не должны содержать
	кодов сигнатуры пакета.
	Структура пакета
	Начало, Команда, данные, Команда, данные,.... Конец пакета.
*/

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow),mPacketState(WaitBegin),mSgnCount(0) { 
    ui->setupUi(this);
	mPort = new QSerialPort(this);
	refreshPortList();
	mSettings.beginGroup(PREF_COM);
	ui->cbPort->setCurrentText(mSettings.value(PREF_PORT_NAME).toString());
	mSettings.endGroup();

	checkBtn();


//	connect(&mPort, SIGNAL(readyRead()), this, SLOT(readByte()));
	connect(ui->tbRefresh, SIGNAL(clicked()),this,SLOT(refreshPortList()));
	connect(ui->btnOpen, SIGNAL(clicked()),this,SLOT(openPort()));
	connect(ui->btnClose, SIGNAL(clicked()),this,SLOT(closePort()));
	connect(mPort,SIGNAL(readyRead()), this, SLOT(readData()));
	
	connect(ui->cbPort->lineEdit(),SIGNAL(returnPressed()),this,SLOT(openPort())); //открытие порта по энтер


	QTest::qExec(this, 0, 0);
}

MainWindow::~MainWindow() {
	mSettings.beginGroup(PREF_COM);
	mSettings.setValue(PREF_PORT_NAME,ui->cbPort->currentText());
	mSettings.endGroup();
    delete ui;
}

void MainWindow::openPort() {	
	mPort->setPortName(ui->cbPort->currentText());
	if (ui->cbPort->currentText().isEmpty()) {
		showError(tr("Не назначено имя порта"));
		return;
	}
	if (!mPort->open(QIODevice::ReadWrite)) {
		showError(mPort->errorString());
			return;
    }
	if (!mPort->setBaudRate(QSerialPort::Baud9600)) {
            showError(mPort->errorString());
            return;
    }
	if (!mPort->setDataBits(QSerialPort::Data8)) {
            showError(mPort->errorString());
            return;
    }
	if (!mPort->setParity(QSerialPort::NoParity)) {
           showError(mPort->errorString());
           return;
     }
	 if (!mPort->setStopBits(QSerialPort::OneStop)) {
           showError(mPort->errorString());
           return;
     }
	 if (!mPort->setFlowControl(QSerialPort::NoFlowControl)) {
          showError(mPort->errorString());
          return;
    }
	 checkBtn();
	 QByteArray ba;
	 ba.append(QChar('f'));
	 mPort->write(ba);
}

void MainWindow::closePort() {
	mPort->close();
	checkBtn();
}


void MainWindow::checkBtn() {
	if (mPort->isOpen()) {
		ui->btnOpen->setEnabled(false);
		ui->btnClose->setEnabled(true);
		ui->cbPort->setEnabled(false);
		ui->tbRefresh->setEnabled(false);
	} else {
		ui->btnOpen->setEnabled(true);
		ui->btnClose->setEnabled(false);
		ui->cbPort->setEnabled(true);
		ui->tbRefresh->setEnabled(true);
	}
}

void MainWindow::refreshPortList() {
	QString st = ui->cbPort->currentText();
	ui->cbPort->clear();
	QList<QSerialPortInfo> &portList = QSerialPortInfo::availablePorts();
	for (int i = 0; i < portList.size();++i)
		ui->cbPort->addItem(portList[i].portName());
	ui->cbPort->setCurrentText(st);
}



void MainWindow::showError(const QString& str) {
	ui->statusbar->showMessage(str,2000);
}

void MainWindow::fillTestData() {
;
}

void MainWindow::testFindSgnPck() {
	//QCOMPARE(findSgnPck(QByteArray("Begin")), 3);
	//QCOMPARE(findSgnPck(QByteArray("infBe")), -1);
	//QCOMPARE(findSgnPck(QByteArray("gTest")), 1);

	//QCOMPARE(parseAnswer(QByteArray("Begin")), QByteArray("in"));
	/*QCOMPARE(parseAnswer(QByteArray("infBe")), QByteArray());
	QCOMPARE(parseAnswer(QByteArray("gTest")), QByteArray("Test"));
	QCOMPARE(parseAnswer(QByteArray("testEndSasha")), QByteArray("Sasha"));*/
	//QCOMPARE(parseAnswer(QByteArray("BegSashaEnd")), QByteArray("test"));
	parseAnswer(QByteArray("BegSashaEnd"));
	QCOMPARE(mBodyPckBuffer,QByteArray("Sasha"));
	parseAnswer(QByteArray("sddddBegSashadEnd"));
	QCOMPARE(mBodyPckBuffer,QByteArray("Sashad"));
	mBodyPckBuffer.clear();
	parseAnswer(QByteArray("Be"));
	QCOMPARE(mBodyPckBuffer,QByteArray(""));
	parseAnswer(QByteArray("gi"));
	QCOMPARE(mBodyPckBuffer,QByteArray(""));
	parseAnswer(QByteArray("ter"));
	QCOMPARE(mBodyPckBuffer,QByteArray("it"));
}

static const QByteArray SignBgnPck = "Beg";
static const QByteArray SignEndPck = "End";
#define SIGNPCKSIZE 3

/*!
	Находит сигнатуру пакета \sng в \ba. Если нашли, возвращает номер позиции за сигнатурой пакета, иначе -1
*/
int MainWindow::findSgnPck(const QByteArray &ba, const QByteArray &sgn) {
	for (int i=0;i<ba.size();++i) {
		if (mSgnCount != 0) { //если прошлый раз мы уже начали распозновать начала пакета
			if (ba[i] == sgn[mSgnCount]) {	//совпадает?
				++mSgnCount; 
				if (mSgnCount==SIGNPCKSIZE) {	//Прошли всю сигнатуру?
				mSgnCount = 0;					//Счётчик байтов сигнатуры в 0
				return i+1;						//возвращаем начало пакета без сигнатуры
			}
			} else {				
				mSgnCount = 0;					//ошибка, сбрасываем счетчик сигнатуры пакета (текущий байт сигнатуры)
			}
				
		} 
		if (ba[i] == sgn[mSgnCount])  //старт отсчета нового пакета
			++mSgnCount;
							
	} //for
	return -1;
}

/*!
	Парсим на предмет сигнатуры пакета. Заполняем \a mBodyPckBuffer телом пакета
*/
void MainWindow::parseAnswer(const QByteArray &ba) {
	if (ba.size() <= 0)
		return ;
	qDebug() << ba;
	if (mPacketState == WaitBegin ) {
		int i = findSgnPck(ba,SignBgnPck);
		if (i!=-1) {
		mPacketState = WaitEnd;			//переводим в режим ожидания конца пакета
		mBodyPckBuffer.clear();
		parseAnswer(ba.right(ba.size()-i));	//обрезаем сигнатуру начала пакета и передаём на обработку дальше
		}
	} 
	else { //если ждём окончания пакета (начало следующего)
		int i = findSgnPck(ba,SignEndPck); //ищем признак конца пакета
		if (i!=-1) {					   // если нашли
		mPacketState = WaitBegin;			//переводим в режим ожидания начала пакета
		mBodyPckBuffer.append(ba.left(i-SIGNPCKSIZE));		//добавляем в буффер всё, что лежит до конца сигнатуры пакета
		qDebug() << mBodyPckBuffer;
		parseAnswer(ba.right(ba.size()-i));		//отправляем на обработку всё что осталось после конца пакета.
		} 
		else {								//иначе отправляем все наши данные кроме последних двух байт (SIGNPCKSIZE) (чтобы быть уверенными что это не признак конца пакета
			if (ba.size() >= SIGNPCKSIZE) {				//а вообще можно отсечь SIGNPCKSIZE символа?
				mBodyPckBuffer.append(ba.left(ba.size()-SIGNPCKSIZE));
				qDebug() << mBodyPckBuffer;
			}
		} //else
	}
}

void MainWindow::readData() {
	parseAnswer(mPort->readAll());
}